/**
 * Plugin which will hide or show an element depending on the rule governing it.
 * 
 * How it works:
 * This plugin attaches event handlers to all the elements which define an 'evaluateElement' class.
 * When the event handler fires, it fetches 'data-evaluation-rule' for all the elements defining an
 * 'evaluateElement' class and evaluates the expression (data-evaluation-rule) if it is present.
 * 
 * @author: Kapil Kashyap
 */
rule_evaluator = (function($) {
	// Global variables.
	var _expressionText 				= null,
		_expressionArray 				= null,
		_reconstructingExpression 		= null,
		_elementObject 					= null,
		_lhsInPlace 					= null,
		_token 							= null,
		_expression 					= null,
		_selector 						= null,
		_elementNotDisabled 			= null,
		_elementIsVisible 				= null,
		_elementCssDisplay 				= null,
		_elementType 					= null,
		_elementValueCheck 				= null,
		_ruleMap						= {},
		// Regular expression variables.
		_operatorRegEx 					= /\s*(~|!=|==|>={0,1}|<={0,1})\s*/g,
		_andRegEx 						= /\s*(and|[&&])\s*/g,
		_orRegEx 						= /\s*(or|[||])\s*/g;

	// Method for evaluating the rule.
	var delegateEvents = function() {
		var _elementEventType = '';
		
		$( '.evaluateElement' ).each(function(index) {
			var _element = $( this ).find( ':input:first' );
			if(_element) {
				if(!_element.attr( 'type' )) {
					if(_element.is( 'textarea' )) {
						_elementType = 'textarea';
					}
					else if(_element.is( 'select' )) {
						_elementType = 'select';
					}
				}
				else {
					_elementType = _element.attr( 'type' );
				}
			}
			
			// Determining the event type to assign to the event handler.
			if(_elementType == 'text' || _elementType == 'textarea') {
				_elementEventType = 'keyup';
			}
			else if(_elementType == 'select' || _elementType == 'select-one') {
				_elementEventType = 'change';
			}
			else {
				_elementEventType = 'click';
			}
			
			// Assigning an event handler for every input element which has 'evaluateElement' class defined.
			if(_elementEventType != null) {
				$( this ).delegate( ':input', _elementEventType, function() {
					_evaluateRule();
				});
			}
		});
	},
		
	generateRuleMap = function() {
		$( '.evaluateElement' ).each(function(elementIndex) {
			var _textStart = null,
				_textEnd = null,
				_textExpression = null,
				_textValue = [];
//				_negateExpression = "";

			_expressionText = $( this ).attr( 'data-evaluation-rule' );

			// Remove the data-evaluation-rule attribute from the form.
			$( this ).removeAttr( 'data-evaluation-rule' );
			
			while(_expressionText.indexOf("^") != -1) {
				_textStart = _expressionText.indexOf("^");
				_textEnd = _expressionText.indexOf("$");
				_textExpression = _expressionText.substring(_textStart, _textEnd + 1);

				_expressionText = _expressionText.replace(_textExpression, 'insertTextExpression');
				_textValue.push(_textExpression.substring(1, _textExpression.length - 1));
			}
			
			if(_expressionText != '') {
//				_negateExpression = "";
//				if(_expressionText.indexOf("!") != -1) {
//					_negateExpression = "!";
//					_expressionText = _expressionText.substring(1, _expressionText.length);
//				}
				// Resetting the variables for every element
				_expressionArray = _expressionText.split(' ');
				_token = null;
				_reconstructingExpression = '';
				_lhsInPlace = false;
				_elementObject = $( this ).find( ':input:first' );
				
				var _counter = 0;
				for(var index = 0; index < _expressionArray.length; index++) {
					_token = _expressionArray[index];

					if(_token.indexOf('(') == 0) {
						_reconstructingExpression += _token + ' ';
					}
					else if(_token.indexOf(')') == 0) {
						_reconstructingExpression += _token + ' ';
					}
					else if(_operatorRegEx.test(_token)) {
						_reconstructingExpression += _token + ' ';
						_lhsInPlace = true;
					}
					else if(_andRegEx.test(_token)) {
						_reconstructingExpression += '&& ';
					}
					else if(_orRegEx.test(_token)) {
						_reconstructingExpression += '|| ';
					}
					else if(_token == 'insertTextExpression') {
						_reconstructingExpression += "'" + _textValue[_counter] + "' ";
						_counter++;
					}
					else {
						_expression = '';
						if(!_lhsInPlace) {
							_selector = '$( \':input[name="' + _token + '"]\' )';
							_elementNotDisabled = _selector + '.is( \':not(:disabled)\' )';
							_elementIsVisible = _selector + '.is( \':visible\' )'; 
							_elementCssDisplay = _selector + '.css( \'display\' ) != \'none\'';
							
							_elementType = eval(_selector + '.attr( \'type\' )');
							_elementValueCheck = '';
							
							if(!_elementType) {
								if(eval(_selector + '.is( \'textarea\' )')) {
									_elementType = 'textarea';
								}
								else if(eval(_selector + '.is( \'select\' )')) {
									_elementType = 'select';
								}
							}

							if(_elementType == 'text' || _elementType == 'textarea' || (_elementType == 'select' || _elementType == 'select-one')) {
								_elementValueCheck = _selector + '.val()';
							}
							else {
								_elementValueCheck = _selector + '.attr( \'checked\' ) == "checked"';
							}
							_expression = _elementNotDisabled + ' && ' + ' ( ' + _elementIsVisible + ' && ' + _elementCssDisplay + ' ) ' + ' && ' + _elementValueCheck;
						}
						else {
							_expression = _token;
						}
						_reconstructingExpression += _expression + ' ';
						_lhsInPlace = false;
					}
				}
			}
			_ruleMap[elementIndex] = _reconstructingExpression;
		});
	},
		
	_evaluateRule = function() {
		$( '.evaluateElement' ).each(function(index) {
			var _elementObject = $( this ).find( ':input:first' );
			
			if(_ruleMap[index]) {
				if(eval(_ruleMap[index])) {
					_elementObject.attr( 'disabled', false);
					$( this ).show();
				}
				else {
					_elementObject.attr( 'disabled', true);
					$( this ).hide();
				}
			}
		});
	};
	
	return {
		initialize: function() {
			delegateEvents();
			generateRuleMap();
			_evaluateRule();
		}
	};
})(jQuery);		
